package com.example.openim.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.example.openim.domain.GroupReviewInfo;
import com.example.openim.dto.BatchReadDto;
import com.example.openim.dto.GroupInfoDto;
import com.example.openim.dto.GroupReviewInfoDto;
import com.example.openim.enums.ErrorCode;
import com.example.openim.enums.ReviewState;
import com.example.openim.service.GroupInfoService;
import com.example.openim.service.TokenCacheService;
import com.example.openim.utils.AjaxResult;
import com.example.openim.utils.StringListUtil;
import com.mongodb.client.result.UpdateResult;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

import static com.example.openim.constant.Constants.*;

/**
 * @description: 群组逻辑
 * @author: xhq
 * @create: 2025-11-07 08:55
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class GroupInfoServiceImpl implements GroupInfoService {

    private final MongoTemplate mongoTemplate;

    private final RestTemplate restTemplate = new RestTemplate();

    @Autowired
    private TokenCacheService tokenCacheService;

    public static final DateTimeFormatter DEFAULT_FORMATTER =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Value("${REVIEW.USER_PHONE}")
    private String REVIEW_USER;

    // 第三方注册接口 URL
    @Value("${REGISTER.URL2}")
    private String URL;

    /**
     * @param creatorUserID :  用户id
     * @return: java.util.List<com.example.openim.dto.GroupReviewInfoDto>
     * @author: ZhangHuixin
     * @date: 2025-11-07 09:17:45
     * @description: 根据用户id查询群组审核信息
     */
    @Override
    public Map queryList(String creatorUserID, Integer page, Integer pageSize) {
        HashMap<String, Object> map = new HashMap<>();

        // 查询条件（复用）
        Criteria criteria = new Criteria().orOperator(
                Criteria.where("review_user_id").is(creatorUserID),
                Criteria.where("creator_user_id").is(creatorUserID)
        );


        // ✅ 1. 分页查询（带排序）
        Query query = new Query(criteria);
        query.with(Sort.by(Sort.Direction.DESC, "createTime"));
        query.skip((long) (page - 1) * pageSize);
        query.limit(pageSize);

        // ✅ 2. 总条数（分页总数）
        long total = mongoTemplate.count(new Query(criteria), GroupReviewInfoDto.class);

        // ✅ 3. 分页数据
        List<GroupReviewInfoDto> list = mongoTemplate.find(query, GroupReviewInfoDto.class);

        // ✅ 4. 全量数据（不分页，用于 count 统计）
        List<GroupReviewInfoDto> allList = mongoTemplate.find(new Query(criteria), GroupReviewInfoDto.class);


        Integer count = 0;
        for (GroupReviewInfoDto group : allList) {
            boolean reviewUser = creatorUserID.equals(group.getReviewUserId());// 是否审核人
            boolean creatorUser = creatorUserID.equals(group.getCreatorUserID());// 是否创建人

            // 如果该数据既是审核人又是创建人 如果其中任意状态为未读 则角标加一
            if (reviewUser && creatorUser) {
                if ("1".equals(group.getReadFlag()) || "1".equals(group.getAdminReadFlag())) {
                    count++;
                }
            } else if (creatorUser && "1".equals(group.getReadFlag())) {
                count++;
            } else if (reviewUser && "1".equals(group.getAdminReadFlag())) {
                count++;
            }

        }
        // ✅ 设置分页数据中的显示标志
        for (GroupReviewInfoDto group : list) {
            boolean isReviewer = creatorUserID.equals(group.getReviewUserId());
            boolean isStatus = "0".equals(group.getReviewState());
            group.setShowReviewButton(isReviewer && isStatus);
        }
        map.put("list", list);
        map.put("count", count);
        map.put("total", total);
        map.put("pageNum", page);
        map.put("pageSize", pageSize);
        return map;
    }


    /**
     * 创建群组添加群组审核信息
     *
     * @param dto
     * @return
     */
    @Override
    public AjaxResult createGroup(GroupInfoDto dto) {
        try {
            List<String> memberUserIDs = dto.getMemberUserIDs();
            if (CollectionUtils.isEmpty(memberUserIDs)) {
                return AjaxResult.error(ErrorCode.ERROR.getCode(), "群成员信息不能为空");
            }
            if (StringUtils.isEmpty(dto.getGroupInfo().getGroupName())) {
                return AjaxResult.error(ErrorCode.ERROR.getCode(), "群名称不能为空");
            }
            GroupReviewInfo groupInfo = dto.getGroupInfo();

            // 群成员ID集合中移除群主ID，避免重复
            if (dto.getOwnerUserID() != null) {
                memberUserIDs.remove(dto.getOwnerUserID());
            }

            //群成员ID集合
            groupInfo.setMemberUsers(String.join(",", memberUserIDs));
            //管理员ID集合
            groupInfo.setAdminUsers(String.join(",", dto.getAdminUserIDs()));
            //群主ID
            groupInfo.setOwnerUserID(dto.getOwnerUserID());
            //创建时间
            groupInfo.setCreateTime(Instant.now().toEpochMilli());
            //创建时间 格式：YYYY-MM-dd HH:mm:ss
            groupInfo.setReviewCreateTime(LocalDateTime.now().format(DEFAULT_FORMATTER));
            //发送消息
            groupInfo.setSendMessage(dto.getSendMessage());
            //默认审核中
            groupInfo.setReviewState(ReviewState.UNDER_REVIEW.getCode());
            //默认已读
            groupInfo.setReadFlag(ErrorCode.SUCCESS.getCode());
            //默认未读
            groupInfo.setAdminReadFlag(ErrorCode.ERROR.getCode());


            //通过配置文件配置审核人手机号获取审核人ID及名称
            Map<String, String> map = getUserInfoByPhoneNumber();
            String userId = null;
            String nickname = null;
            if (map != null) {
                userId = map.get("userId");
                nickname = map.get("nickname");
            }
            groupInfo.setReviewUserId(userId);
            groupInfo.setReviewUserName(nickname);
            GroupReviewInfo insert = mongoTemplate.insert(groupInfo);
            if (StringUtils.isEmpty(insert.getId())) {
                return AjaxResult.error(ErrorCode.ERROR.getCode(), "创建失败");
            }
            return AjaxResult.success("创建成功");
        } catch (Exception e) {
            return AjaxResult.error(ErrorCode.ERROR.getCode(), "创建失败");
        }
    }


    /**
     * 通过手机号查询用户ID和昵称
     *
     * @return 包含用户ID和昵称的Map，格式：{userId: xxx, nickname: xxx}
     */
    public Map<String, String> getUserInfoByPhoneNumber() {
        String phoneNumber = null;
        try {
            Map one = mongoTemplate.findOne(new Query(), Map.class,"review_user_info");
            if (one != null ) {
                phoneNumber = (String) one.get("phone");
            }else {
                REVIEW_USER = phoneNumber;
            }
            // 1. 根据手机号查询credential集合
            Query credentialQuery = new Query(Criteria.where("account").is(phoneNumber));
            Map credential = mongoTemplate.findOne(credentialQuery, Map.class, "credential");

            if (credential == null || credential.get("user_id") == null) {
                log.warn("未找到手机号对应的凭证信息: {}", phoneNumber);
                return null;
            }

            // 2. 获取用户ID
            String userId = credential.get("user_id").toString();

            // 3. 根据user_id关联查询user表
            Query userQuery = new Query(Criteria.where("user_id").is(userId));
            Map user = mongoTemplate.findOne(userQuery, Map.class, "user");

            if (user == null || user.get("nickname") == null) {
                log.warn("未找到用户ID对应的用户信息: {}", userId);
                return null;
            }

            // 4. 构造返回结果
            Map<String, String> result = new HashMap<>();
            result.put("userId", userId);
            result.put("nickname", user.get("nickname").toString());

            return result;
        } catch (Exception e) {
            log.error("通过手机号查询用户信息失败，手机号: {}", phoneNumber, e);
            return null;
        }
    }


    /**
     * @param groupReviewInfo :
     * @return: java.lang.Boolean
     * @author: ZhangHuixin
     * @date: 2025-11-07 14:51:52
     * @description: 审核
     */
    @Override
    public AjaxResult audit(HttpServletRequest request, GroupReviewInfo groupReviewInfo) {
        // 参数校验
        if (StringUtils.isBlank(groupReviewInfo.getId()) && StringUtils.isBlank(groupReviewInfo.getReviewState())) {
            return AjaxResult.error(ErrorCode.ERROR.getCode(), "参数不完整");
        }

        // 查询待审核数据
        GroupReviewInfo group = mongoTemplate.findById(groupReviewInfo.getId(), GroupReviewInfo.class);
        if (Objects.isNull(group)) {
            return AjaxResult.error(ErrorCode.ERROR.getCode(), "数据不存在");
        }

        // 权限验证
        if (!groupReviewInfo.getCreatorUserID().equals(group.getReviewUserId())) {
            return AjaxResult.error(ErrorCode.ERROR.getCode(), "非该数据审核人，无权审核该数据");
        }

        // 状态检查
        if (!"0".equals(group.getReviewState())) {
            return AjaxResult.error(ErrorCode.ERROR.getCode(), "该记录已审核，无法重复操作");
        }

        // 获取用户token
        String token = getUserToken(group.getCreatorUserID(), 7);
        if (StringUtils.isBlank(token)) {
            return AjaxResult.error(ErrorCode.ERROR.getCode(), "获取用户token失败");
        }

        // 根据审核状态处理
        return switch (groupReviewInfo.getReviewState()) {
            // 通过
            case "1" -> handleApproved(group, groupReviewInfo, token);
            // 不通过
            case "2" -> handleRejected(groupReviewInfo);
            default -> AjaxResult.error(ErrorCode.ERROR.getCode(), "无效的审核状态");
        };
    }

    /**
     * 飘零已读
     * @param dto
     * @return
     */
    /**
     * 批量设置群组审核信息为已读状态
     *
     * @param dto 群组审核信息ID集合
     * @return 是否更新成功
     */
    public AjaxResult batchRead(BatchReadDto dto) {
        try {
            List<String> ids = dto.getIds();
            String userId = dto.getUserId();
            if (CollectionUtils.isEmpty(ids)) {
                return AjaxResult.error(ErrorCode.ERROR.getCode(), "参数不能为空");
            }
            // 构建批量更新条件
            Query query = new Query(Criteria.where("creator_user_id").is(userId));
            // 构建更新内容
            Update update = new Update().set("read_flag", "0");
            // 执行批量更新
            UpdateResult result = mongoTemplate.updateMulti(query, update, GroupReviewInfo.class);
            // 构建批量更新条件
            Query adminQuery = new Query(Criteria.where("review_user_id").is(userId));
            // 构建更新内容
            Update adminUpdate = new Update().set("admin_read_flag", "0");
            // 执行批量更新
            UpdateResult adminResult = mongoTemplate.updateMulti(adminQuery, adminUpdate, GroupReviewInfo.class);
            return AjaxResult.success(true);
        } catch (Exception e) {
            return AjaxResult.error(ErrorCode.ERROR.getCode(), "已读失败");
        }
    }



    private AjaxResult handleApproved(GroupReviewInfo group, GroupReviewInfo reviewInfo, String token) {
        return createGroupWithRetry(group, reviewInfo, token);
    }

    private AjaxResult handleRejected(GroupReviewInfo reviewInfo) {
        boolean updateSuccess = updateReviewState(reviewInfo);
        return updateSuccess
                ? AjaxResult.success("审核已驳回")
                : AjaxResult.error(ErrorCode.ERROR.getCode(), "审核驳回失败");
    }

    private AjaxResult createGroupWithRetry(GroupReviewInfo groupInfo, GroupReviewInfo reviewInfo, String token) {
        AjaxResult first = createGroupOnce(groupInfo, token);
        if (first.isSuccess()) {
            boolean updated = updateReviewState(reviewInfo);
            return updated ? AjaxResult.success("审核通过，群组创建成功") : AjaxResult.error(ErrorCode.ERROR.getCode(), "群组创建失败，审核未生效");
        }
        if (StringUtils.isNotBlank(first.getCode()) && first.getCode().startsWith("150")) {
            tokenCacheService.clearUserTokenCache(groupInfo.getCreatorUserID(), 7);
            String newToken = getUserToken(groupInfo.getCreatorUserID(), 7);
            if (StringUtils.isBlank(newToken)) {
                return AjaxResult.error(first.getCode(), "创建失败，需要重新获取token再次创建");
            }
            AjaxResult second = createGroupOnce(groupInfo, newToken);
            if (second.isSuccess()) {
                boolean updated = updateReviewState(reviewInfo);
                return updated ? AjaxResult.success("审核通过，群组创建成功") : AjaxResult.error(ErrorCode.ERROR.getCode(), "群组创建失败，审核未生效");
            }
            if (StringUtils.isNotBlank(second.getCode()) && second.getCode().startsWith("150")) {
                return AjaxResult.error(second.getCode(), "创建失败，需要重新获取token再次创建");
            }
            return AjaxResult.error(ErrorCode.ERROR.getCode(), "群组创建失败，审核未生效");
        }
        return AjaxResult.error(ErrorCode.ERROR.getCode(), "群组创建失败，审核未生效");
    }

    private AjaxResult createGroupOnce(GroupReviewInfo groupInfo, String token) {
        try {
            GroupInfoDto groupInfoDto = buildGroupInfoDto(groupInfo);
            HttpHeaders headers = new HttpHeaders();
            headers.set("operationID", UUID.randomUUID().toString());
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("token", token);
            HttpEntity<GroupInfoDto> entity = new HttpEntity<>(groupInfoDto, headers);
            ResponseEntity<String> response = restTemplate.exchange(
                    URL + CREATE_GROUP_URL,
                    HttpMethod.POST,
                    entity,
                    String.class
            );
            log.info("群组创建请求结果: {}", response.getBody());
            return parseCreateGroupResponse(response.getBody());
        } catch (Exception e) {
            log.error("群组创建请求出现异常错误", e);
            return AjaxResult.error(ErrorCode.ERROR.getCode(), "群组创建失败");
        }
    }

    private GroupInfoDto buildGroupInfoDto(GroupReviewInfo groupInfo) {
        GroupInfoDto groupInfoDto = new GroupInfoDto();
        groupInfoDto.setGroupInfo(groupInfo);
        groupInfoDto.setMemberUserIDs(StringListUtil.safeSplitToList(groupInfo.getMemberUsers(), ","));
        groupInfoDto.setAdminUserIDs(StringListUtil.safeSplitToList(groupInfo.getAdminUsers(), ","));
        groupInfoDto.setOwnerUserID(groupInfo.getOwnerUserID());
        groupInfoDto.setSendMessage(groupInfo.getSendMessage());
        return groupInfoDto;
    }

    private AjaxResult parseCreateGroupResponse(String responseBody) {
        JSONObject jsonObject = JSONObject.parseObject(responseBody);
        String errCode = jsonObject.getString("errCode");
        if ("0".equals(errCode)) {
            return AjaxResult.success("创建成功");
        }
        String errMsg = jsonObject.getString("errMsg");
        return AjaxResult.error(errCode, errMsg);
    }

    private boolean updateReviewState(GroupReviewInfo group) {
        try {
            Query query = new Query(Criteria.where("_id").is(group.getId()));
            Update update = new Update()
                    .set("review_time", LocalDateTime.now().format(DEFAULT_FORMATTER))
                    .set("review_state", group.getReviewState())
                    .set("review_opinion", group.getReviewOpinion()) // 可选
                    .set("read_flag", "1"); // 可选

            UpdateResult result = mongoTemplate.updateFirst(query, update, GroupReviewInfo.class);
            boolean success = result.getModifiedCount() > 0;
            log.info("ID: {}, 状态: {} → {}", group.getId(), "0", group.getReviewState());
            return success;
        } catch (Exception e) {
            log.error("更新审核状态失败，ID: {}", group.getId(), e);
            return false;
        }
    }


    /**
     * Method to obtain an admin token for authentication
     * 优先从Redis缓存获取，缓存不存在时再请求HTTP接口
     *
     * @return String representing the admin token, or null if request fails
     */
    private String getAdminToken() {
        // 优先从Redis缓存获取管理员token
        String cachedToken = tokenCacheService.getAdminToken();
        if (StringUtils.isNotBlank(cachedToken)) {
            return cachedToken;
        }

        // 缓存不存在，请求HTTP接口获取token
        log.info("Redis缓存中未找到管理员token，开始请求HTTP接口获取");

        Map<String, Object> requestData = Map.of(
                "secret", "openIM123",
                "userID", "imAdmin"
        );

        String response = executeTokenRequest(URL + GET_ADMIN_TOKEN, requestData, null);
        log.info("获取管理员token结果： {}", response);
        return processAdminTokenResponse(response);
    }

    private String processAdminTokenResponse(String response) {
        if (StringUtils.isBlank(response)) {
            return null;
        }

        JSONObject jsonObject = JSONObject.parseObject(response);
        if ("0".equals(jsonObject.getString("errCode"))) {
            JSONObject data = JSONObject.parseObject(jsonObject.getString("data"));
            String token = data.getString("token");

            // 将获取到的token缓存到Redis，设置85天过期时间
            if (StringUtils.isNotBlank(token)) {
                tokenCacheService.cacheAdminToken(token, 85L * 24 * 60 * 60);
            }

            return token;
        }

        return null;
    }

    /**
     * @param userId:     用户id
     * @param platformID: 1：iOS，2：Android，3：Windows，4：OSX，5：WEB，6：小程序，7：linux，8：AndroidPad，9：IPad，10：Admin
     * @return: java.lang.String
     * @author: ZhangHuixin
     * @date: 2025-11-10 09:07:18
     * @description: 通过管理员token获取用户token
     * 优先从Redis缓存获取，缓存不存在时再请求HTTP接口
     */
    private String getUserToken(String userId, Integer platformID) {
        // 优先从Redis缓存获取用户token
        String cachedToken = tokenCacheService.getUserToken(userId, platformID);
        if (StringUtils.isNotBlank(cachedToken)) {
            return cachedToken;
        }

        // 缓存不存在，获取管理员token后请求用户token
        log.info("Redis缓存中未找到用户token，开始获取管理员token并请求用户token，userId: {}, platformID: {}", userId, platformID);

        String adminToken = getAdminToken();
        if (StringUtils.isBlank(adminToken)) {
            log.error("获取管理员token失败，无法获取用户token");
            return null;
        }

        Map<String, Object> requestData = Map.of(
                "platformID", platformID,
                "userID", userId
        );

        String response = executeTokenRequest(URL + GET_USER_TOKEN, requestData, adminToken);
        log.info("获取用户{}  token结果： {}", userId, response);

        if (StringUtils.isBlank(response)) {
            return null;
        }

        JSONObject jsonObject = JSONObject.parseObject(response);
        String errCode = jsonObject.getString("errCode");
        if ("0".equals(errCode)) {
            return processUserTokenResponse(response, userId, platformID);
        }

        if (StringUtils.isNotBlank(errCode) && errCode.startsWith("150")) {
            tokenCacheService.clearAdminTokenCache();
            String freshAdminToken = getAdminToken();
            if (StringUtils.isBlank(freshAdminToken)) {
                return null;
            }
            String retryResponse = executeTokenRequest(URL + GET_USER_TOKEN, requestData, freshAdminToken);
            log.info("刷新管理员token后重试获取用户{} token结果： {}", userId, retryResponse);
            return processUserTokenResponse(retryResponse, userId, platformID);
        }

        return null;
    }

    private String processUserTokenResponse(String response, String userId, Integer platformID) {
        if (StringUtils.isBlank(response)) {
            return null;
        }

        JSONObject jsonObject = JSONObject.parseObject(response);
        if ("0".equals(jsonObject.getString("errCode"))) {
            JSONObject data = JSONObject.parseObject(jsonObject.getString("data"));
            String token = data.getString("token");

            // 将获取到的用户token缓存到Redis，设置85天过期时间
            if (StringUtils.isNotBlank(token)) {
                tokenCacheService.cacheUserToken(userId, platformID, token, 85L * 24 * 60 * 60);
            }

            return token;
        }

        return null;
    }

    /**
     * 执行token请求的统一方法
     */
    private String executeTokenRequest(String url, Map<String, Object> requestData, String token) {
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.set("operationID", UUID.randomUUID().toString());
            headers.setContentType(MediaType.APPLICATION_JSON);
            if (StringUtils.isNotBlank(token)) {
                headers.set("token", token);
            }

            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestData, headers);

            ResponseEntity<String> response = restTemplate.exchange(
                    url,
                    HttpMethod.POST,
                    entity,
                    String.class
            );

            return response.getBody();
        } catch (Exception e) {
            log.error("Token请求失败，URL: {}", url, e);
            return null;
        }
    }
}
